﻿using System.Linq;
using RSSReader.classesCustom;
using System.Collections.Generic;
using System;

namespace RSSReader.classesCustom
{


    /// <summary>
    /// class for paging objects returns only one page of result 
    /// specified in 'sfilter'
    /// from article List 'artList'
    /// no modifications allowed, for new set of data new object creation required
    /// </summary>
    public class ArticlePager
    {
        private readonly List<Article> articleList;
        private readonly ArticlePagingValidator pvalidator;
        private readonly Int32 pageNumber, pageSize;

        public ArticlePager(ArticleCriteriaInString sfilter, List<Article> artList)
        {
            pvalidator = new ArticlePagingValidator(artList.Count);
            this.articleList = artList;
            this.pageNumber = pvalidator.validatePageNumber(sfilter.pageNumber, sfilter.pageSize);
            this.pageSize = pvalidator.validatePageSize(sfilter.pageSize);
        }

        public List<Article> getArticlePage()
        {
            //todo: errorchecking

            List<Article> articlePage = null;
            //get required page
            if (articleList.Count > 0)
            {
                int startArt = Math.Min(articleList.Count - 1, (pageNumber - 1) * pageSize);
                int nrArt = Math.Min(articleList.Count - (pageNumber - 1) * pageSize, pageSize);
                articlePage = (List<Article>)articleList.GetRange(startArt, nrArt);
            }

            return articlePage;
        }
    }

    /// <summary>
    /// class for paging validation objects
    /// </summary>
    class ArticlePagingValidator
    {
        private readonly Int32 pageSizeMin, pageSizeMax, pageSizeDefault;
        private readonly Int32 pageNumberMin, pageNumberMax, pageNumberDefault;
        private readonly Int32 recordCount;

        public ArticlePagingValidator(Int32 value)
        {
            this.recordCount = value;
            this.pageSizeMin = 3;
            this.pageSizeMax = recordCount;
            this.pageSizeDefault = 12;
            this.pageNumberMin = 1;
            this.pageNumberMax = recordCount / pageSizeMin;
            this.pageNumberDefault = 1;
        }

        public Int32 validatePageSize(Int32 pSize)
        {
            if (pSize < pageSizeMin || pSize > pageSizeMax)
                throw new Exception("required pageSize not valid...");
            else
                return pSize;
        }

        public Int32 validatePageSize(String pSize)
        {
            Int32 result = 0;
            try
            {
                result = validatePageSize(Convert.ToInt32(pSize));
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                result = pageSizeDefault;
            }
            return result;
        }

        public Int32 validatePageNumber(Int32 pNumber, Int32 pSize)
        {
            if (pNumber < pageNumberMin || pNumber > pageNumberMax)
                throw new Exception("required pageNumber outside limits...");
            else
                if (pNumber * pSize - recordCount > pSize)
                    throw new Exception("required pageNumber is not valid...");
                else
                    return pNumber;
        }

        public Int32 validatePageNumber(String pNumber, String pSize)
        {
            Int32 result = 0;
            try
            {
                result = validatePageNumber(Convert.ToInt32(pNumber), Convert.ToInt32(pSize));
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                result = pageNumberDefault;
            }
            return result;
        }
    }

    /// <summary>
    /// class for filtering objects, which filter liust of articles 'artList'
    /// according to parameters specified in 'sfilter'
    /// no modifications allowed, for new set of data new object creation required
    /// </summary>
    public class ArticleFilter
    {
        private readonly List<Article> articleList;
        private readonly ArticleFilterValidator fvalidator;        
        private readonly DateTime date;
        private readonly String category, title, description;


        public ArticleFilter(ArticleCriteriaInString sfilter, List<Article> artList)
        {
            fvalidator = new ArticleFilterValidator();
            this.articleList = artList;
            this.category = sfilter.category;
            this.title = sfilter.title;
            this.description = sfilter.description;
            this.date = fvalidator.validateDate(sfilter.date);
        }

        public List<Article> getFilteredArticleList()
        {
            List<Article> articlePage=null,a = null;
            try
            {
                //filter articles
                articlePage = (List<Article>)articleList
                    .Where(p => p.title.ToLower().Contains(title.ToLower()))
                    .Where(p => p.category.ToLower().Contains(category.ToLower()))
                    .Where(p => p.description.ToLower().Contains(description.ToLower()))
                    .Where(p => p.pubdate.CompareTo(date) > 0)
                    .ToList();
                //articlePage = (List<Article>)articleList.Where(p => p.title.ToLower().Contains(title.ToLower())).ToList();
                //articlePage = (List<Article>)articleList.Where(p => p.category.ToLower().Contains(category.ToLower())).ToList();
                //articlePage = (List<Article>)articleList.Where(p => p.description.ToLower().Contains(description.ToLower())).ToList();
                //articlePage = (List<Article>)articleList.Where(p => p.pubdate.CompareTo(date) > 0).ToList();
                //a = (List<Article>)articleList.Where(p => p.pubdate.CompareTo(date) <= 0).ToList();
                //articlePage = (List<Article>)articleList.ToList();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            return articlePage;
        }
    }

    /// <summary>
    /// validates filter values to be sure that validation is done against valid values
    /// constant values could be injected from config files?!?
    /// </summary>
    class ArticleFilterValidator
    {
        private readonly DateTime dateMin, dateMax, dateDefault;

        public ArticleFilterValidator()
        {
            this.dateMin = new DateTime(2013, 12, 1);
            this.dateMax = DateTime.Now;
            this.dateDefault = this.dateMin;
        }

        public DateTime validateDate(DateTime date)
        {
            if (date < dateMin || date > dateMax)
                throw new Exception("article date not in required range...");
            else
                return date;
        }

        public DateTime validateDate(String date)
        {
            DateTime result;
            String[] d, ymd, hms;

            try
            {
                d = date.Split(' ');
                ymd = d[0].Split('-');
                hms = d[1].Split(':');
                result = new DateTime(Convert.ToInt32(ymd[0]), Convert.ToInt32(ymd[1]), Convert.ToInt32(ymd[2]),
                            Convert.ToInt32(hms[0]), Convert.ToInt32(hms[1]), Convert.ToInt32(hms[2]));
                result = validateDate(result);
            }
            catch (IndexOutOfRangeException e)
            {
                Console.WriteLine(e.Message);
                result = dateDefault;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                result = dateDefault;
            }
            return result;
        }
    }
}